package main

import (
	"fmt"
	"sync"
	"time"
)

/**
 * @Classname p1
 * @Description TODO
 * @author cjf
 * @Date 2021/7/14 14:50
 * @Version V1.0
 */

/*
# 简单的
- 作业简单的题:实现一个线程安全的集合set，元素是string
- 要求有 NewSet方法初始化
- Add方法添加元素，添加重复元素可以去重
- Del方法删除元素
- Merge方法合并另一个set
- PrintElement 方法打印所有元素
- JudgeElement方法 检测输入的string 是否存在于set中
- 总之就是set相关的方法
- https://github.com/deckarep/golang-set

*/

type SafeSet struct {
	sync.RWMutex
	m map[string]struct{}
}

func NewSet() *SafeSet {
	return &SafeSet{
		m: make(map[string]struct{}),
	}
}

//增加
func (ss *SafeSet) Add(key string) {
	ss.Lock()
	defer ss.Unlock()
	ss.m[key] = struct{}{}
}

//删除
func (ss *SafeSet) Del(key string) {
	ss.Lock()
	defer ss.Unlock()
	delete(ss.m, key)
}

//判断
func (ss *SafeSet) JudgeElement(key string) bool {
	ss.RLock()
	defer ss.RUnlock()
	_, ok := ss.m[key]
	return ok
}

//打印
func (ss *SafeSet) PrintElement() []string {
	ss.RLock()
	defer ss.RUnlock()
	res := make([]string, 0)
	for k := range ss.m {
		res = append(res, k)
	}
	return res
}

//合并
func (ss *SafeSet) Merge(set *SafeSet) {
	ss.Lock()
	defer ss.Unlock()
	keys := set.PrintElement()
	for _, k := range keys {
		ss.m[k] = struct{}{}
	}
}

//增加集合的成员
func setAdd(set *SafeSet, n int) {
	for i := 0; i < n; i++ {
		key := fmt.Sprintf("key_%d", i)
		set.Add(key)
	}
}

//减少集合的成员
func setDel(set *SafeSet, n int) {
	for i := 0; i < n; i++ {
		key := fmt.Sprintf("key_%d", i)
		set.Del(key)
	}
}

func main() {

	//基础测试
	//创建一个Set集合
	s1 := NewSet()
	setAdd(s1, 10)

	//打印s1Set集合中的成员
	fmt.Println(s1.PrintElement())

	//删除s1 Set中的5个成员
	setDel(s1, 5)
	fmt.Println(s1.PrintElement())

	//创建另一个Set集合
	s2 := NewSet()
	setAdd(s2, 20)

	//s1和新创建的s2的成员去重合并
	s1.Merge(s2)

	//打印s1Set集合中的成员
	fmt.Println(s1.PrintElement())

	//测试线程安全
	go setAdd(s1, 100)
	go setDel(s1, 100)
	go fmt.Println(s1.PrintElement())

	go setAdd(s1, 200)
	go setDel(s1, 200)
	go fmt.Println(s1.PrintElement())

	go setAdd(s1, 300)
	go setDel(s1, 300)
	go fmt.Println(s1.PrintElement())

	time.Sleep(10 * time.Second)

}
